package com.huluobo.lc.libnetwork;

import android.annotation.SuppressLint;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.arch.core.executor.ArchTaskExecutor;

import com.huluobo.lc.libnetwork.cache.CacheManager;

import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;

/**
 * @author Lc
 * @description: 功能:发起具体请求,T代表返回中的实体类型, R代表request的子类
 * @date :2020/7/9 11:37
 */
public abstract class Request<T, R extends Request> implements Cloneable {
    private static final String TAG = "Request";
    protected String url;//需要传入url
    protected HashMap<String, String> headers = new HashMap<>();//请求头,
    protected HashMap<String, Object> params = new HashMap<>();//参数

    public static final int CACHE_ONLY = 1;//只缓存
    public static final int CACHE_FIRST = 2;//先访问缓存,再访问网络
    public static final int NET_ONLY = 3;//只访问网络
    public static final int NET_CACHE = 4;//网络请求之后,缓存到本地
    private String cacheKey;
    private Type type;
    private Class claz;
    private int cacheStrategy;

    @IntDef({CACHE_ONLY, CACHE_FIRST, NET_ONLY, NET_CACHE})
    public @interface CacheStrategy {

    }

    //添加url构造方法,必传
    public Request(String url) {
        this.url = url;
    }

    //添加头的方法,构建者模式,返回本身
    public R addHeader(String key, String value) {
        headers.put(key, value);
        return (R) this;
    }

    //添加参数的方法,构建者模式,返回本身
    public R addParam(String key, Object value) {
        if (value == null) {
            return (R) this;
        }
        //int string byte char
        //验证Object必须为基本数据类型,通过反射的方式,获取到TYPE
        try {
            if (value.getClass() == String.class) {
                params.put(key, value);
            } else {
                Field field = value.getClass().getField("TYPE");
                Class claz = (Class) field.get(null);
                if (claz.isPrimitive()) {//如果通过反射拿到的type是基本数据类型
                    params.put(key, value);
                }
            }
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return (R) this;
    }

    //获取缓存的方式
    public R cacheKey(String key) {
        this.cacheKey = key;
        return (R) this;
    }

    //获取具体的缓存策略,是否通过网络缓存
    public R cacheStrategy(@CacheStrategy int cacheStrategy) {
        this.cacheStrategy = cacheStrategy;
        return (R) this;
    }

    //执行请求,异步的,有callback
    @SuppressLint("RestrictedApi")
    public void execute(final JsonCallback<T> callback) {
        if (cacheStrategy != NET_ONLY) {
            //在子线程中执行
            ArchTaskExecutor.getIOThreadExecutor().execute(new Runnable() {
                @Override
                public void run() {
                    ApiResponse<T> response = readCache();
                    if (callback != null) {
                        callback.onCacheSuccess(response);
                    }
                }
            });
        }
        if (cacheStrategy != CACHE_ONLY) {
            getCall().enqueue(new Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull IOException e) {
                    ApiResponse<T> response = new ApiResponse<>();
                    response.message = e.getMessage();
                    callback.onError(response);
                }

                @Override
                public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                    ApiResponse<T> apiResponse = parseResponse(response, callback);
                    if (apiResponse.success) {
                        callback.onSuccess(apiResponse);
                    } else {
                        callback.onError(apiResponse);
                    }
                }
            });
        }
    }

    private ApiResponse<T> parseResponse(Response response, JsonCallback<T> callback) {
        String message = null;
        int status = response.code();
        boolean success = response.isSuccessful();
        ApiResponse<T> result = new ApiResponse<>();
        Convert convert = ApiService.convert;
        try {
            String content = response.body().string();
            if (success) {
                if (callback != null) {
                    ParameterizedType type =
                            (ParameterizedType) callback.getClass().getGenericSuperclass();
                    Type argument = type.getActualTypeArguments()[0];//拿到Bean类的类型
                    result.body = (T) convert.convert(content, argument);
                } else if (type != null) {
                    result.body = (T) convert.convert(content, type);
                } else if (claz != null) {
                    result.body = (T) convert.convert(content, claz);
                } else {
                    Log.e(TAG, "parseResponse:无法解析");
                }
            } else {
                message = content;
            }
        } catch (IOException e) {
            message = e.getMessage();
            success = false;
        }

        result.success = success;
        result.status = status;
        result.message = message;

        if (cacheStrategy != NET_ONLY && result.success && result.body != null && result.body instanceof Serializable) {
            saveCache(result.body);
        }
        return result;
    }

    //通过key读取缓存
    private ApiResponse<T> readCache() {
        String key = TextUtils.isEmpty(cacheKey) ? generateCacheKey() : cacheKey;
        Object cache = CacheManager.getCache(key);
        ApiResponse<T> result = new ApiResponse<>();
        result.status = 304;
        result.message = "获取缓存成功";
        result.body = (T) cache;
        result.success = true;
        return result;
    }

    //保存缓存到ROOM数据库
    private void saveCache(T body) {
        String key = TextUtils.isEmpty(cacheKey) ? generateCacheKey() : cacheKey;
        CacheManager.save(key, body);
    }

    //通过URL生成一个缓存的键
    private String generateCacheKey() {
        cacheKey = UrlCreator.createUrlFromParams(url, params);
        return cacheKey;
    }

    //拿到异步请求的返回
    private Call getCall() {
        okhttp3.Request.Builder builder = new okhttp3.Request.Builder();
        addHeaders(builder);//取出所有header中的key,value,并且给builder赋值
        okhttp3.Request request = generateRequest(builder);
        Call call = ApiService.okHttpClient.newCall(request);
        return call;
    }

    //创建request的方法,需要子类实现
    protected abstract okhttp3.Request generateRequest(okhttp3.Request.Builder builder);

    //添加请求头的方法,添加到具体的builder中
    private void addHeaders(okhttp3.Request.Builder builder) {
        for (Map.Entry<String, String> entry : headers.entrySet()) {
            builder.addHeader(entry.getKey(), entry.getValue());//循环取出headers
        }
    }

    //执行请求,同步的,没有callback,通过实现Cloneable接口,使当前的网络请求可以产生正确的返回值
    public ApiResponse<T> execute() {
        if (cacheStrategy == CACHE_ONLY) {
            return readCache();
        }

        ApiResponse<T> result = null;
        try {
            Response response = getCall().execute();
            result = parseResponse(response, null);
        } catch (IOException e) {
            e.printStackTrace();
            if (result == null) {
                result = new ApiResponse<>();
                result.message = e.getMessage();
            }
        }
        return result;
    }

    //外部应用时,传入具体的Bean的类型
    public R responseType(Type type) {
        this.type = type;
        return (R) this;
    }

    //外部应用时,传入具体的Bean的类型
    public R responseType(Class claz) {
        this.claz = claz;
        return (R) this;
    }

    @NonNull
    @Override
    public Request clone() throws CloneNotSupportedException {
        return (Request<T, R>) super.clone();
    }
}
